#include <iostream>
#include <queue>
#include <unordered_map>
#include <functional>
#include <shared_mutex>
#include <condition_variable>
#include <thread>
#include <chrono>
#include <memory>
#include <atomic>
#include <typeindex>
#include <utility>
#include <algorithm>

class EventQueue{
    public:
    using EventHandler=std::function<void(const void *)>;
    using TimerId=uint64_t;

    private:
    struct EventBase{
        virtual ~EventBase()=default;
        virtual const std::type_index & getType() const=0;
    };

    template<typename T>
    struct EventWrapper : public EventBase{
        T data;
        EventWrapper(T && data) : data(std::move(data)){}

        const std::type_index & getType() const override{
            static std::type_index type=typeid(T);
            return type;
        }
    };

    struct DelayedEvent{
        std::chrono::system_clock::time_point time;
        std::shared_ptr<EventBase> event;
        TimerId id;
    };

    struct DelayedEventCompare{
        bool operator()(const DelayedEvent & lhs, const DelayedEvent & rhs) const{
            return lhs.time > rhs.time;
        }
    };

    std::shared_mutex mutex_;
    std::shared_mutex sync_mutex_;
    std::condition_variable_any cv_;
    std::unordered_map<std::type_index, std::vector<EventHandler>> subscribers_;
    std::queue<std::shared_ptr<EventBase>> event_queue_;
    std::priority_queue<DelayedEvent, std::vector<DelayedEvent>, DelayedEventCompare> delayed_events_;
    std::unordered_map<TimerId, bool> active_timers_;
    std::atomic<TimerId> next_timer_id_{1};
    std::atomic<bool> running_{true};
    std::thread dispatch_thread_;

    void processDelayedEvents(){
        auto now=std::chrono::system_clock::now();

        while(!delayed_events_.empty() && delayed_events_.top().time <= now){
            auto delayed_event=delayed_events_.top();
            delayed_events_.pop();

            if(active_timers_[delayed_event.id]){
                event_queue_.push(delayed_event.event);
                active_timers_.erase(delayed_event.id);
            }
        }
    }

    public:
    EventQueue(){
        // Start the dispatch thread
        dispatch_thread_=std::thread([this](){
            while(running_){
                std::shared_ptr<EventBase> event;

                {
                    std::unique_lock<std::shared_mutex> lock(mutex_);
                    processDelayedEvents();

                    if(event_queue_.empty()){
                        if(!delayed_events_.empty()){
                            auto next_time=delayed_events_.top().time;
                            cv_.wait_until(lock, next_time);
                        } else{
                            cv_.wait(lock);
                        }
                        continue;
                    }

                    event=event_queue_.front();
                    event_queue_.pop();
                }

                const auto & type=event->getType();
                std::vector<EventHandler> handlers_copy;
                {
                    std::unique_lock<std::shared_mutex> lock(mutex_);
                    auto it=subscribers_.find(type);
                    if(it != subscribers_.end()){
                        handlers_copy=it->second;
                    }
                }

                for(auto & handler : handlers_copy){
                    handler(event.get());
                }
            }
        });
    }

    ~EventQueue(){
        running_=false;
        cv_.notify_all();
        if(dispatch_thread_.joinable()){
            dispatch_thread_.join();
        }
    }

    template<typename T>
    void publish(T && event){
        std::unique_lock<std::shared_mutex> lock(mutex_);
        auto wrapper=std::make_shared<EventWrapper<T>>(std::forward<T>(event));
        event_queue_.push(wrapper);
        cv_.notify_one();
    }

    template<typename T>
    TimerId publishDelayed(T && event, std::chrono::milliseconds delay){
        std::unique_lock<std::shared_mutex> lock(mutex_);
        TimerId id=next_timer_id_++;
        auto wrapper=std::make_shared<EventWrapper<T>>(std::forward<T>(event));
        auto time=std::chrono::system_clock::now() + delay;

        delayed_events_.push({time, wrapper, id});
        active_timers_[id]=true;
        cv_.notify_one();

        return id;
    }

    bool cancelDelayed(TimerId id){
        std::unique_lock<std::shared_mutex> lock(mutex_);
        if(active_timers_.count(id)){
            active_timers_[id]=false;
            return true;
        }
        return false;
    }

    template<typename T>
    void subscribe(std::function<void(const T &)> handler){
        std::unique_lock<std::shared_mutex> lock(mutex_);
        subscribers_[typeid(T)].emplace_back([handler](const void * event){
            handler(*static_cast<const T *>(event));
        });
    }

    template<typename T>
    bool waitForEvent(T & result, std::chrono::milliseconds timeout){
        std::unique_lock<std::shared_mutex> lock(sync_mutex_);
        std::condition_variable cv;
        bool received=false;

        EventHandler handler=[&](const void * event){
            std::unique_lock<std::shared_mutex> res_lock(sync_mutex_);
            result=*static_cast<const T *>(event);
            received=true;
            res_lock.unlock();
            cv.notify_one();
        };

        {
            std::unique_lock<std::shared_mutex> sub_lock(mutex_);
            subscribers_[typeid(T)].emplace_back(handler);
        }

        auto success=cv.wait_for(lock, timeout, [&](){ return received; });

        {
            std::unique_lock<std::shared_mutex> sub_lock(mutex_);
            auto & handlers=subscribers_[typeid(T)];
            handlers.erase(std::remove(handlers.begin(), handlers.end(), handler), handlers.end());
        }

        return success;
    }
};